/**
* AbstractCollectionValueHolder - A partial implementation of a CollectionValueHolder Interface
*
* Copyright (c) 2002
* Marty Phelan, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package com.taursys.model;
import java.util.Iterator;
import java.util.Collection;
import javax.swing.event.ChangeListener;
import java.util.ArrayList;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.util.Collections;
import com.taursys.debug.Debug;
/**
* A partial implementation of a CollectionValueHolder Interface.
* This class uses an internal ObjectValueHolder and Collection to
* implement the required contract.
* @author Marty Phelan
* @version 2.0
*/
public class AbstractCollectionValueHolder implements CollectionValueHolder,
ChangeListener {
private Collection collection;
private Iterator iterator;
private ValueHolder parentValueHolder = null;
private String parentPropertyName = null;
private com.taursys.model.ObjectValueHolder objectValueHolder;
/**
* Constructs a new AbstractCollectionValueHolder
*/
public AbstractCollectionValueHolder(ObjectValueHolder holder) {
this(holder, new ArrayList());
}
/**
* Constructs a new AbstractCollectionValueHolder for the given collection.
*/
public AbstractCollectionValueHolder(ObjectValueHolder holder,
Collection collection) {
objectValueHolder = holder;
setCollection(collection);
}
// ====================================================================
// PROXY METHODS TO INTERNAL OBJECT VALUE HOLDER
// ValueHolder Interface Methods
// ====================================================================
/**
* Get the internal ObjectValueHolder for this CollectionValueHolder.
* The internal ObjectValueHolder is specified in the constructor.
* @return the internal ObjectValueHolder for this CollectionValueHolder.
*/
protected ObjectValueHolder getObjectValueHolder() {
return objectValueHolder;
}
/**
* Get the value of the given property in the valueObject.
* @return the value of the given property in the valueObject.
*/
public Object getPropertyValue(String propertyName) throws ModelException {
return objectValueHolder.getPropertyValue(propertyName);
}
/**
* Get the values for the given property names.
* This method returns an empty Object array if the given propertyNames
* is null or empty.
* @param propertyNames array of property names
* @return the values for the given property names.
*/
public Object[] getPropertyValues(String[] propertyNames)
throws ModelException {
return objectValueHolder.getPropertyValues(propertyNames);
}
/**
* Set the value of the given property in the valueObject.
* Fires a StateChanged event to any listeners.
* @param propertyName the property name to set
* @param value the value to set the property to
*/
public void setPropertyValue(String propertyName, Object value)
throws ModelException {
objectValueHolder.setPropertyValue(propertyName, value);
}
/**
* Set the values for the given properties in the valueObject.
* Fires a StateChanged event to any listeners.
* @param propertyNames the property names to set
* @param values the values to set the properties to
*/
public void setPropertyValues(String[] propertyNames, Object[] values)
throws ModelException {
objectValueHolder.setPropertyValues(propertyNames, values);
}
/**
* Get the java data type for the given property
* @return the java data type for the given property
*/
public int getJavaDataType(String propertyName) throws ModelException {
return objectValueHolder.getJavaDataType(propertyName);
}
/**
* Get the alias name for this ValueHolder. This property is used by the
* ComponentFactory to bind Components to ValueHolders by matching it to the
* first part of the Component's ID property.
* @return the alias name for this ValueHolder
*/
public String getAlias() {
return objectValueHolder.getAlias();
}
/**
* Sets the alias name for this value holder. This property is used by the
* ComponentFactory to bind Components to value holders by matching it to the
* first part of the Component's ID property.
* @param newAlias the alias name for this value holder
*/
public void setAlias(String newAlias) {
objectValueHolder.setAlias(newAlias);
}
/**
* Removes the specified change listener so that it no longer receives change
* events from this value holder. Change events are generated whenever the
* contents of the value holder change.
* @param l the change listener to remove
*/
public void removeChangeListener(ChangeListener l) {
objectValueHolder.removeChangeListener(l);
}
/**
* Adds the specified change listener to receive change events from this
* value holder. Change events are generated whenever the contents of the
* value holder change.
* @param l the change listener to add
*/
public void addChangeListener(ChangeListener l) {
objectValueHolder.addChangeListener(l);
}
/**
* Returns the object in the current position. You should ensure that the
* current position is valid before invoking this method.
*/
public Object getObject() {
return objectValueHolder.getObject();
}
/**
* Replaces the object in the current position with the given one. You should
* ensure that the current position is valid before invoking this method.
* The order of unordered collections may be disturbed by this operation.
*/
public void setObject(Object obj) {
Object old = getObject();
objectValueHolder.setObject(obj);
collection.remove(old);
collection.add(obj);
}
// ***********************************************************************
// * PARENT RELATED METHODS
// ***********************************************************************
/**
* Set the parent ValueHolder for this VOCollectionValueHolder.
* The parent ValueHolder will provide the Collection for this ValueHolder.
* If the parent is also a CollectionValueHolder, then whenever the
* parent moves to a new row, this VOCollectionValueHolder will receive
* a notification and will retrieve its new collection from the parent.
* @param parentValueHolder the parent ValueHolder for this VOCollectionValueHolder.
*/
public void setParentValueHolder(ValueHolder parentValueHolder) {
if (parentValueHolder != null)
parentValueHolder.removeChangeListener(this);
this.parentValueHolder = parentValueHolder;
if (parentValueHolder != null)
parentValueHolder.addChangeListener(this);
}
/**
* Get the parent ValueHolder for this VOCollectionValueHolder.
* The parent ValueHolder will provide the Collection for this ValueHolder.
* If the parent is also a CollectionValueHolder, then whenever the
* parent moves to a new row, this VOCollectionValueHolder will receive
* a notification and will retrieve its new collection from the parent.
* @return the parent ValueHolder for this VOCollectionValueHolder.
*/
public ValueHolder getParentValueHolder() {
return parentValueHolder;
}
/**
* Set the property name of the Collection in the parentValueHolder for this
* VOCollectionValueHolder.
* The parent ValueHolder will provide the Collection for this ValueHolder.
* If the parent is also a CollectionValueHolder, then whenever the
* parent moves to a new row, this VOCollectionValueHolder will receive
* a notification and will retrieve its new collection from the parent.
* @param parentValueHolder the property name of the Collection in the
* parentValueHolder for this VOCollectionValueHolder.
*/
public void setParentPropertyName(String parentPropertyName) {
this.parentPropertyName = parentPropertyName;
}
/**
* Get the property name of the Collection in the parentValueHolder for this
* VOCollectionValueHolder.
* The parent ValueHolder will provide the Collection for this ValueHolder.
* If the parent is also a CollectionValueHolder, then whenever the
* parent moves to a new row, this VOCollectionValueHolder will receive
* a notification and will retrieve its new collection from the parent.
* @return the property name of the Collection in the parentValueHolder
* for this VOCollectionValueHolder.
*/
public String getParentPropertyName() {
return parentPropertyName;
}
/**
* Invoked by the parentValueHolder whenever there is a change in its value.
* The parent ValueHolder will provide the Collection for this ValueHolder.
* If the parent is also a CollectionValueHolder, then whenever the
* parent moves to a new row, this VOCollectionValueHolder will receive
* a notification and will retrieve its new collection from the parent.
* @param e the ChangeEvent from the parentValueHolder
*/
public void stateChanged(ChangeEvent e) {
try {
if (parentPropertyName != null && parentPropertyName.length() > 0) {
Collection c = (Collection)parentValueHolder.getPropertyValue(
parentPropertyName);
setCollection(c);
}
} catch (Exception ex) {
com.taursys.debug.Debug.error(
"Problem getting new collection from parent",ex);
setCollection(null);
}
}
// ***********************************************************************
// * POSITION RELATED METHODS
// * CollectionValueHolder Interface
// ***********************************************************************
/**
* Indicates whether there is another (any) Objects in the collection.
* If the internal iterator for the collection has not yet been created,
* this method will invoke the getInternalIterator method. This method
* returns the results of the iterator's hasNext method.
*/
public boolean hasNext() {
if (getInternalIterator() != null)
return iterator.hasNext();
else
return false;
}
/**
* Makes the next object in the collection available. You should invoke
* the hasNext method BEFORE invoking this method to ensure that there IS
* a next object. The ValueObject is fetched from the collection and stored in
* the valueObject property.
*/
public void next() {
if (getInternalIterator() != null)
objectValueHolder.setObject(iterator.next());
else
objectValueHolder.setObject(null);
}
/**
* Resets this holder so that you can iterate the collection from the beginning.
* This sets the iterator and valueObject properties to null. That ensures
* that the iterator is recreated at the next invocation of hasNext,
* next, or getInternalIterator.
*/
public void reset() {
iterator = null;
objectValueHolder.setObject(null);
}
/**
* Returns the current iterator, or creates one if the iterator is null.
* Returns null if the collection is null.
*/
private Iterator getInternalIterator() {
if (iterator == null)
if (getCollection() != null)
iterator = getCollection().iterator();
return iterator;
}
// ====================================================================
// PROXY METHODS TO INTERNAL COLLECTION
// ====================================================================
/**
* Returns the current collection of this holder.
*/
public Collection getCollection() {
return collection;
}
/**
* Sets the current collection for this holder and invokes reset method.
* If the given collection is null, an Collections.EMPTY_LIST is stored
* instead, otherwise the given collection is stored. This is done to prevent
* NullPointerExceptions from occuring whenever any method is subsequently
* called.
*/
public void setCollection(Collection collection) {
this.collection = collection == null ? Collections.EMPTY_LIST : collection;
reset();
}
/**
* Returns the number of elements in the underlying collection of this holder.
* If the underlying collection of this holder
* contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
*
* @return the number of elements in the underlying collection of this holder
*/
public int size() {
return collection.size();
};
/**
* Returns <tt>true</tt> if the underlying collection of this holder contains no elements.
*
* @return <tt>true</tt> if the underlying collection of this holder contains no elements
*/
public boolean isEmpty() {
return collection.isEmpty();
}
/**
* Returns <tt>true</tt> if the underlying collection of this holder contains the specified
* element. More formally, returns <tt>true</tt> if and only if this
* collection contains at least one element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in the underlying collection of this holder is to be tested.
* @return <tt>true</tt> if the underlying collection of this holder contains the specified
* element
*/
public boolean contains(Object o) {
return collection.contains(o);
}
/**
* Returns an iterator over the elements in the underlying collection of this holder.
* There are no guarantees concerning the order in which the elements are returned
* (unless the underlying collection of this holder is an instance of some class that provides a
* guarantee).
*
* @return an <tt>Iterator</tt> over the elements in the underlying collection of this holder
*/
public Iterator iterator() {
return collection.iterator();
}
/**
* Returns an array containing all of the elements in the underlying collection
* of this holder. If the collection makes any guarantees as to what order
* its elements are returned by its iterator, this method must return the
* elements in the same order.<p>
*
* The returned array will be "safe" in that no references to it are
* maintained by the underlying collection of this holder. (In other words, this method must
* allocate a new array even if the underlying collection of this holder is backed by an array).
* The caller is thus free to modify the returned array.<p>
*
* This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in the underlying collection of this holder
*/
public Object[] toArray() {
return collection.toArray();
}
/**
* Returns an array containing all of the elements in the underlying collection of this holder
* whose runtime type is that of the specified array. If the collection
* fits in the specified array, it is returned therein. Otherwise, a new
* array is allocated with the runtime type of the specified array and the
* size of the underlying collection of this holder.<p>
*
* If the underlying collection of this holder fits in the specified array with room to spare
* (i.e., the array has more elements than the underlying collection of this holder), the element
* in the array immediately following the end of the collection is set to
* <tt>null</tt>. This is useful in determining the length of this
* collection <i>only</i> if the caller knows that the underlying collection of this holder does
* not contain any <tt>null</tt> elements.)<p>
*
* If the underlying collection of this holder makes any guarantees as to what order its elements
* are returned by its iterator, this method must return the elements in
* the same order.<p>
*
* Like the <tt>toArray</tt> method, this method acts as bridge between
* array-based and collection-based APIs. Further, this method allows
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs<p>
*
* Suppose <tt>l</tt> is a <tt>List</tt> known to contain only strings.
* The following code can be used to dump the list into a newly allocated
* array of <tt>String</tt>:
*
* <pre>
* String[] x = (String[]) v.toArray(new String[0]);
* </pre><p>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
*
* @param a the array into which the elements of the underlying collection of this holder are to be
* stored, if it is big enough; otherwise, a new array of the same
* runtime type is allocated for this purpose.
* @return an array containing the elements of the underlying collection of this holder
*
* @throws ArrayStoreException the runtime type of the specified array is
* not a supertype of the runtime type of every element in this
* collection.
*/
public Object[] toArray(Object[] a) {
return collection.toArray(a);
}
/**
* Ensures that the underlying collection of this holder contains the specified element (optional
* operation). Returns <tt>true</tt> if the underlying collection of this holder changed as a
* result of the call. (Returns <tt>false</tt> if the underlying collection of this holder does
* not permit duplicates and already contains the specified element.)<p>
*
* Collections that support this operation may place limitations on what
* elements may be added to the underlying collection of this holder. In particular, some
* collections will refuse to add <tt>null</tt> elements, and others will
* impose restrictions on the type of elements that may be added.
* Collection classes should clearly specify in their documentation any
* restrictions on what elements may be added.<p>
*
* If a collection refuses to add a particular element for any reason
* other than that it already contains the element, it <i>must</i> throw
* an exception (rather than returning <tt>false</tt>). This preserves
* the invariant that a collection always contains the specified element
* after this call returns.
*
* @param o element whose presence in the underlying collection of this holder is to be ensured.
* @return <tt>true</tt> if the underlying collection of this holder changed as a result of the
* call
*
* @throws UnsupportedOperationException add is not supported by this
* collection.
* @throws ClassCastException class of the specified element prevents it
* from being added to the underlying collection of this holder.
* @throws IllegalArgumentException some aspect of this element prevents
* it from being added to the underlying collection of this holder.
*/
public boolean add(Object o) {
return collection.add(o);
}
/**
* Removes a single instance of the specified element from this
* collection, if it is present (optional operation). More formally,
* removes an element <tt>e</tt> such that <tt>(o==null ? e==null :
* o.equals(e))</tt>, if the underlying collection of this holder contains one or more such
* elements. Returns true if the underlying collection of this holder contained the specified
* element (or equivalently, if the underlying collection of this holder changed as a result of the
* call).
*
* @param o element to be removed from the underlying collection of this holder, if present.
* @return <tt>true</tt> if the underlying collection of this holder changed as a result of the
* call
*
* @throws UnsupportedOperationException remove is not supported by this
* collection.
*/
public boolean remove(Object o) {
return collection.remove(o);
}
/**
* Returns <tt>true</tt> if the underlying collection of this holder contains all of the elements
* in the specified collection.
*
* @param c collection to be checked for containment in the underlying collection of this holder.
* @return <tt>true</tt> if the underlying collection of this holder contains all of the elements
* in the specified collection
* @see #contains(Object)
*/
public boolean containsAll(Collection c) {
return collection.containsAll(c);
}
/**
* Adds all of the elements in the specified collection to the underlying collection of this holder
* (optional operation). The behavior of this operation is undefined if
* the specified collection is modified while the operation is in progress.
* (This implies that the behavior of this call is undefined if the
* specified collection is the underlying collection of this holder, and the underlying collection of this holder is
* nonempty.)
*
* @param c elements to be inserted into the underlying collection of this holder.
* @return <tt>true</tt> if the underlying collection of this holder changed as a result of the
* call
*
* @throws UnsupportedOperationException if the underlying collection of this holder does not
* support the <tt>addAll</tt> method.
* @throws ClassCastException if the class of an element of the specified
* collection prevents it from being added to the underlying collection of this holder.
* @throws IllegalArgumentException some aspect of an element of the
* specified collection prevents it from being added to this
* collection.
*
* @see #add(Object)
*/
public boolean addAll(Collection c) {
return collection.addAll(c);
}
/**
*
* Removes all the underlying collection of this holder's elements that are also contained in the
* specified collection (optional operation). After this call returns,
* the underlying collection of this holder will contain no elements in common with the specified
* collection.
*
* @param c elements to be removed from the underlying collection of this holder.
* @return <tt>true</tt> if the underlying collection of this holder changed as a result of the
* call
*
* @throws UnsupportedOperationException if the <tt>removeAll</tt> method
* is not supported by the underlying collection of this holder.
*
* @see #remove(Object)
* @see #contains(Object)
*/
public boolean removeAll(Collection c) {
return collection.removeAll(c);
}
/**
* Retains only the elements in the underlying collection of this holder that are contained in the
* specified collection (optional operation). In other words, removes from
* the underlying collection of this holder all of its elements that are not contained in the
* specified collection.
*
* @param c elements to be retained in the underlying collection of this holder.
* @return <tt>true</tt> if the underlying collection of this holder changed as a result of the
* call
*
* @throws UnsupportedOperationException if the <tt>retainAll</tt> method
* is not supported by this Collection.
*
* @see #remove(Object)
* @see #contains(Object)
*/
public boolean retainAll(Collection c) {
return collection.retainAll(c);
}
/**
* Removes all of the elements from the underlying collection of this holder (optional operation).
* This collection will be empty after this method returns unless it
* throws an exception.
*
* @throws UnsupportedOperationException if the <tt>clear</tt> method is
* not supported by the underlying collection of this holder.
*/
public void clear() {
collection.clear();
}
}